<?php

namespace Hilaoyu\LaravelExtend\Services\AccessControl\Traits;

use Hilaoyu\LaravelExtend\Services\Auth\AuthService;
use Hilaoyu\Utils\UtilSupport;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

trait ModelWithAccessControlFilterTrait
{
    protected $_access_auto_set_owner = true;

    abstract public function accessGetAccessIdentifier():string ;

    public function accessGetOwnerFieldName(){
        return 'owner_id';
    }
    public function accessGetOwnerTypeFieldName(){
        return 'owner_model';
    }

    public function accessAutoSetOwner($auto =true){
        $this->_access_auto_set_owner = (bool)$auto;
        return $this;
    }

    public function accessGetAutoSetOwner(){
        return $this->_access_auto_set_owner;
    }

    public function access_owner(){
        return $this
            ->morphTo(
                'access_owner'
                ,"model_with_access_control_filter_trait_owner_model_name"
                ,$this->accessGetOwnerFieldName()
            );
    }

    public function accessGetOwnerId(){
        return $this->{$this->accessGetOwnerFieldName()};
    }

    public function accessGetOwner(){
        return $this->access_owner()->first();
    }



    public function accessSetOwner($owner = null){

        if(!$owner){
            $owner = UtilSupport::tryQuiet(function (){
                return AuthService::AuthenticatedUser();
            });
        }

        if($owner instanceof Model){
            $this->{$this->accessGetOwnerFieldName()} = $owner->getKey();
            $this->{$this->accessGetOwnerTypeFieldName()} = class_basename($owner);
        }



        return $this;
    }


    public function eventOnSavingModelWithAccessControlFilterTrait(){
        //$status = parent::eventOnSaving();

        if($this->accessGetAutoSetOwner() && !$this->accessGetOwnerId()) {
            return $this->accessSetOwner();
        }

        return true;
    }
    public function eventOnConstructModelWithAccessControlFilterTrait(){
        $this->append([
            "model_with_access_control_filter_trait_owner_model_name"
        ]);
        $this->makeHidden(['model_with_access_control_filter_trait_owner_model_name']);
        //$this->addHidden([$this->accessGetOwnerFieldName(),$this->accessGetOwnerTypeFieldName()]);
    }

    public function getModelWithAccessControlFilterTraitOwnerModelNameAttribute(){
        $ownerModelName = $this->getAttribute($this->accessGetOwnerTypeFieldName());
        if($ownerModelName){
            $ownerModelName = Str::beforeLast(static::class,'\\') . '\\'.$ownerModelName;
        }
        return $ownerModelName;
    }

    public function accessControlLimitHasCount($owner){
        return $this->newModelQuery()->withAccessOwnerFilter($owner)->count();
    }


    public function scopeWithAccessFilter($query,$access_identifier = '',$owner = null){
        $access_identifier = trim($access_identifier);
        if(empty($access_identifier)){
            $access_identifier = $this->accessGetAccessIdentifier();
        }

        if(empty($access_identifier)){
            return $query;
        }


        if(is_null($owner)){
            $owner = UtilSupport::tryQuiet(function (){
                return AuthService::AuthenticatedUser();;
            });
        }

        /*if(!$owner){
            return $query->whereRaw('1=0');
        }*/

        try{
            if($owner->can('view_all',$access_identifier)){
                return $query;
            }elseif($owner->can('view_group',$access_identifier)){


                /*$same_group_user_ids = UtilSupport::tryQuiet(function () use ($owner){
                    return $owner->getSameRoleModels()->pluck('id');
                });
                return $query->withAccessOwnerFilter($same_group_user_ids);*/
                return $query->withAccessOwnerFilter(function ($afQuery) use ($owner){

                    $owner_table_name = $owner->getConnection()->getTablePrefix() . $owner->getTable();
                    $this_table_name = $this->getConnection()->getTablePrefix() . $this->getTable();

                    $subQuery = $owner->newQuery()
                        ->select(DB::raw(1))
                        ->withSameRolesFilter()
                        ->whereRaw($owner_table_name.'.id = '.$this_table_name.'.'.$this->accessGetOwnerFieldName())
                        ->toBase();

                    $afQuery = $afQuery->addWhereExistsQuery($subQuery,'and');

                    /*$afQuery = $afQuery->whereExists(function ($subQuery) use ($owner){
                        dd($subQuery);
                        $subQuery->select(DB::raw(1))
                            ->from($owner->getTable())
                            ->withSameRolesFilter()
                            ->whereRaw($owner->getTable().'.id = '.$this->getTable().'.id');

                        //$subQuery = $owner->newQuery()->withSameRolesFilter()->toBase();
                    });*/

                    //dd($afQuery->toRawSql());
                    return $afQuery;
                });

            }elseif($owner->can('view_own',$access_identifier)){
                return $query->withAccessOwnerFilter($owner);
            }

            return $query->whereRaw('1=0');

        }catch (\Exception $e){
            //dd($e);
            $this->setError($e->getMessage());
            return $query->whereRaw('1=0');
        }

    }

    public function scopeWithAccessOwnerFilter($query,$owner=null){
        if(is_null($owner)){
            $owner = UtilSupport::tryQuiet(function (){
                return AuthService::AuthenticatedUser();;
            });
        }

        if(!$owner){
            $query = $query->whereRaw('1=0');
        }elseif(is_string($owner)){
            $query =  $query->where($this->accessGetOwnerFieldName(),'=',trim($owner));
        }elseif($owner instanceof Model){
            if($owner_id = UtilSupport::tryQuiet(function () use($owner){
                return $owner->getAuthIdentifier();
            })){
                $query =  $query->where($this->accessGetOwnerFieldName(),'=',$owner_id);
            }else{
                $query = $query->whereRaw('1=0');
            }

        }elseif(is_array($owner) || $owner instanceof Arrayable){
            $query =  $query->whereIn($this->accessGetOwnerFieldName(),$owner);
        }else{
            $query = $query->where($owner);
        }

        return $query;

    }


}
